package test;

import java.math.BigDecimal;
import java.math.BigInteger;


public class Encoder {


    public static int encode(Short value, byte[] bytes, int offset) {
        if (value == null) {
            bytes[offset] = Constants.NULL_BYTE_HIGH;
            return 1;
        } else {
            bytes[offset] = Constants.NOT_NULL_BYTE_HIGH;
            encode(value.shortValue(), bytes, offset + 1);
            return 3;
        }
    }


    public static void encode(short value, byte[] bytes, int offset) {
        value ^= 0x8000;
        bytes[offset] = (byte) (value >> 8);
        bytes[offset + 1] = (byte) value;
    }


    public static int encode(Integer value, byte[] bytes, int offset) {
        if (value == null) {
            bytes[offset] = Constants.NULL_BYTE_HIGH;
            return 1;
        } else {
            bytes[offset] = Constants.NOT_NULL_BYTE_HIGH;
            encode(value.intValue(), bytes, offset + 1);
            return 5;
        }
    }


    public static void encode(int value, byte[] bytes, int offset) {
        value ^= 0x80000000;
        bytes[offset] = (byte) (value >> 24);
        bytes[offset + 1] = (byte) (value >> 16);
        bytes[offset + 2] = (byte) (value >> 8);
        bytes[offset + 3] = (byte) value;
    }


    public static int encode(Long value, byte[] bytes, int offset) {
        if (value == null) {
            bytes[offset] = Constants.NULL_BYTE_HIGH;
            return 1;
        } else {
            bytes[offset] = Constants.NOT_NULL_BYTE_HIGH;
            encode(value.longValue(), bytes, offset + 1);
            return 9;
        }
    }


    public static void encode(long value, byte[] bytes, int offset) {
        int $ = ((int) (value >> 32)) ^ 0x80000000;
        bytes[offset] = (byte) ($ >> 24);
        bytes[offset + 1] = (byte) ($ >> 16);
        bytes[offset + 2] = (byte) ($ >> 8);
        bytes[offset + 3] = (byte) $;
        $ = (int) value;
        bytes[offset + 4] = (byte) ($ >> 24);
        bytes[offset + 5] = (byte) ($ >> 16);
        bytes[offset + 6] = (byte) ($ >> 8);
        bytes[offset + 7] = (byte) $;
    }


    public static int encode(Float value, byte[] bytes, int offset) {
        if (value == null) {
            bytes[offset] = Constants.NULL_BYTE_HIGH;
            return 1;
        } else {
            encode(value.floatValue(), bytes, offset);
            return 5;
        }
    }


    public static void encode(float value, byte[] bytes, int offset) {
        int bits = Float.floatToIntBits(value);
        bytes[offset] = (byte) (bits >> 24);
        bytes[offset + 1] = (byte) (bits >> 16);
        bytes[offset + 2] = (byte) (bits >> 8);
        bytes[offset + 3] = (byte) bits;
    }


    public static int encode(Double value, byte[] bytes, int offset) {
        if (value == null) {
            bytes[offset] = Constants.NULL_BYTE_HIGH;
            return 1;
        } else {
            encode(value.doubleValue(), bytes, offset++);
            return 9;
        }
    }


    public static void encode(double value, byte[] bytes, int offset) {
        long bits = Double.doubleToLongBits(value);
        int w = (int) (bits >> 32);
        bytes[offset] = (byte) (w >> 24);
        bytes[offset + 1] = (byte) (w >> 16);
        bytes[offset + 2] = (byte) (w >> 8);
        bytes[offset + 3] = (byte) w;
        w = (int) bits;
        bytes[offset + 4] = (byte) (w >> 24);
        bytes[offset + 5] = (byte) (w >> 16);
        bytes[offset + 6] = (byte) (w >> 8);
        bytes[offset + 7] = (byte) w;
    }

    public static int encode(BigDecimal value, byte[] bytes, int offset) {
        if (value == null) {
            bytes[offset] = Constants.NULL_BYTE_HIGH;
            return 1;
        }
        String plainString = value.toPlainString();
        byte[] plainStringBytes = plainString.getBytes();
        int length = encodeLength(plainStringBytes.length, bytes, offset);
        System.arraycopy(plainStringBytes, 0, bytes, offset + 1 + length, plainStringBytes.length);
        return offset + 1 + length + plainStringBytes.length;
    }

    public static int encode(BigInteger value, byte[] bytes, int offset) {
        if (value == null) {
            bytes[offset] = Constants.NULL_BYTE_HIGH;
            return 1;
        }
        String plainString = value.toString();
        byte[] plainStringBytes = plainString.getBytes();
        int length = encodeLength(plainStringBytes.length, bytes, offset);
        System.arraycopy(plainStringBytes, 0, bytes, offset + 1 + length, plainStringBytes.length);
        return offset + 1 + length + plainStringBytes.length;
    }

    public static int encode(String value, byte[] bytes, int offset) {
        if (value == null) {
            bytes[offset] = Constants.NULL_BYTE_HIGH;
            return 1;
        }
        String plainString = value;
        byte[] plainStringBytes = plainString.getBytes();
        int length = encodeLength(plainStringBytes.length, bytes, offset);
        System.arraycopy(plainStringBytes, 0, bytes, offset + 1 + length, plainStringBytes.length);
        return offset + 1 + length + plainStringBytes.length;
    }


    private static int encodeLength(int value, byte[] bytes, int offset) {
        System.out.println("value:" + value);
        if (value < 128) {//2^7
            bytes[offset++] = (byte) 1;
            bytes[offset] = (byte) value;
            return 1;
        } else if (value < 16384) {//128*128
            bytes[offset++] = (byte) 2;
            bytes[offset++] = (byte) ((value >> 8) | 0x80);
            bytes[offset] = (byte) value;
            return 2;
        } else if (value < 2097152) {//128*128*128
            bytes[offset++] = (byte) 3;
            bytes[offset++] = (byte) ((value >> 16) | 0xc0);
            bytes[offset++] = (byte) (value >> 8);
            bytes[offset] = (byte) value;
            return 3;
        } else if (value < 268435456) {//128*128*128*128
            bytes[offset++] = (byte) 4;
            bytes[offset++] = (byte) ((value >> 24) | 0xe0);
            bytes[offset++] = (byte) (value >> 16);
            bytes[offset++] = (byte) (value >> 8);
            bytes[offset] = (byte) value;
            return 4;
        } else {
            bytes[offset++] = (byte) 5;
            bytes[offset++] = (byte) 0xf0;
            bytes[offset++] = (byte) (value >> 24);
            bytes[offset++] = (byte) (value >> 16);
            bytes[offset++] = (byte) (value >> 8);
            bytes[offset] = (byte) value;
            return 5;
        }
    }

    private static int getLength(int value) {
        if (value < 128) {//2^7
            return 1;
        } else if (value < 16384) {//128*128
            return 2;
        } else if (value < 2097152) {//128*128*128
            return 3;
        } else if (value < 268435456) {//128*128*128*128
            return 4;
        } else {
            return 5;
        }
    }


}
